import path from 'node:path';
import fs from 'node:fs';
import { createHash } from 'node:crypto'
import { readdir, stat, writeFile as writeFilePromises,  readFile as readFilePromises } from "node:fs/promises"
import { minimatch } from 'minimatch'

/**
 * 检查路径是否应该被排除
 * @param {string} pathname 路径
 * @param {Array<string>} patterns 排除模式数组
 * @returns {boolean} 是否应该排除
 */
function shouldExclude(pathname: string, patterns: Array<string>): boolean {
	return patterns.some(pattern => {
		// 支持简单的文件/文件夹名称匹配
		if (!pattern.includes('*') && !pattern.includes('?')) {
			return pathname === pattern || pathname.endsWith(path.sep + pattern)
		}
		// 支持 glob 模式匹配
		return minimatch(pathname, pattern, { matchBase: true })
	})
}

/**
 * 异步读取目录下所有文件信息
 * @param {string} directory 读取路径
 * @param {string} base	基础路径
 * @param {Array<string>} exclude 排除项
 * @returns {Promise<Array<IDirectory>>} 文件信息数组promise
 */
export async function readDirectory(directory:string, base?:string, exclude:Array<string>=[]){
	const files: Array<IDirectory> = []
	if(!base) base = directory.split('\\').at(-1)
	return new Promise(async (resolve,reject) => {
		try{
			const folder = await readdir(directory)
			for(let i = 0; i< folder.length;i++){
				const name = folder[i]
				const filePath = path.join(directory, folder[i])
				const statement = await stat(filePath)
				const relative = path.join((base as string), name)
				const type = statement.isFile() ? 'file' : 'folder'
				
				// 检查是否应该排除
				if(shouldExclude(name, exclude) || shouldExclude(relative, exclude)) continue;
				
				const data = { name, path: filePath, relative, type }
				if(type === 'folder') data['children'] = await readDirectory(filePath, relative, exclude)
				files.push(data)
			}
			resolve(files)
		}catch(e){ reject(e) }
	})
}

/**
 * 同步读取目录下所有文件信息
 * @param {string} directory 读取路径
 * @param {string} base	基础路径
 * @param {Array<string>} exclude 排除项
 * @returns {Array<IDirectory>} 文件信息数组
 */
export function readDirectorySync(directory: string, base?:string, exclude:Array<string>=[]){
	const files: Array<IDirectory> = []
	if(!base) base = directory.split('\\').at(-1) as string
	const folder = fs.readdirSync(directory)
	for(let i = 0; i< folder.length;i++){
		const name = folder[i]
		const filePath = path.join(directory, folder[i])
		const stat = fs.statSync(filePath)
		const relative = path.join(base, name)
		const type = stat.isFile() ? 'file' : 'folder'
		
		// 检查是否应该排除
		if(shouldExclude(name, exclude) || shouldExclude(relative, exclude)) continue;
		
		const data = { name, path: filePath, relative, type }
		if(type === 'folder'){
			data['children'] = readDirectorySync(filePath, relative, exclude)
		}
		files.push(data)
	}
	return files
}

/**
 * MD5 加密
 * @param { string } str 加密字符串
 * @returns { string } MD5加密字符
 */
export function md5(str: string){
	const hash = createHash('md5')
	hash.update(str)
	return hash.digest('hex')
}

/**
 * 读取文件内容
 * @param { string } path 文件路径
 * @returns { Promise<string> } 读取结果
 */
export function readFile(path:string): Promise<string>{
	return readFilePromises(path, { encoding: 'utf-8'})
}

/**
 * 文件写入
 * @param { string } path 文件路径
 * @param { string } content 文件内容
 * @returns 
 */
export function writeFile(path: string, content: string){
	return writeFilePromises(path, content, { encoding: 'utf-8' })
}

/**
 * 同步文件写入
 * @param { string } path 文件路径
 * @param { string } content 文件内容
 * @returns 
 */
export function writeFileSync(path: string, content: string){
	fs.writeFileSync(path, content, 'utf8');
}